package com.hyts.network.service.nio;

import com.hyts.network.config.DefaultNetwork;
import com.hyts.network.core.Client;
import com.hyts.network.error.ConnectionException;

import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.logging.Logger;

/**
 * project-name:coffice-ext-network
 * package-name:com.hyts.ext.network.service
 * author:Libo/Alex
 * create-date:2018-11-24 10:06
 * copyright:libo-hyts-github
 * email:libo2dev@aliyun.com
 * description:此类主要用于:  nio服务操作传参 和 出参
 */
public abstract class NioClient<P,R> extends Client<P,R,SocketChannel> {

    /**
     * field-name: logger
     * field-type: Logger
     * description: 日志输出器
     */
    private Logger logger = Logger.getLogger(this.getClass().getName());

    public void setBlock(boolean block) {
        this.block = block;
    }

    private boolean block;

    private boolean status;

    private P param;

    public NioClient(String ip, int port) {
        super(ip, port);
        this.block = false;
    }

    public NioClient(SocketChannel client) {
        super(client);
    }


    public NioClient() {
        this.block = true;
    }


    /**
     * 建立监听
     */
    private void listener(Selector selector) throws IOException {
        logger.info("建立客户端监听.....");
        while(status){
            if(selector.select(DefaultNetwork.DEFAULT_CONNECTION_TIMEOUT) == 0){
                logger.info("重试继续建立客户端连接.....");
                continue;
            }
            Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
            while(ite.hasNext()){
                SelectionKey selectionKey = ite.next();
                if(selectionKey.isConnectable() && selectionKey.isValid()){
                	SocketChannel client = (SocketChannel) selectionKey.channel();
                    if(client.isConnectionPending()){
                        handlerConnection(selectionKey);
                    }
                    else if(selectionKey.isReadable()){
                        byte [] result = handlerRead(selectionKey);
                        System.out.println("123123123------------"+result);
                    }
                }
            }
        }
    }

    /**
     * 关闭连接
     * @return
     */
    public boolean close(){
        if(this.getClient() != null){
            try {
                this.getClient().close();
            } catch (IOException e) {
                return false;
            }
        }
        return true;
    }


    @Override
    public R execute(P param) {
        //connect();
        ByteBuffer buffer = ByteBuffer.allocate(DefaultNetwork.DEFAULT_BUFFEER_SIZE);
            try (
                 SocketChannel socketChannel = SocketChannel.open();
                 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                 ObjectOutputStream oss = new ObjectOutputStream(byteArrayOutputStream);
            ){
                socketChannel.configureBlocking(true);
                this.setClient(socketChannel);
                //Selector selector = Selector.open();
                //getClient().register(selector, SelectionKey.OP_CONNECT);
                logger.info("采用阻塞模式：");
                System.out.println(socketChannel.isConnected()+" "+socketChannel.isConnectionPending());
                socketChannel.connect(new InetSocketAddress(getIp(),getPort()));
                //listener(selector);
                if(socketChannel.finishConnect()){
                    logger.info("客户端发送信息："+param);
                    oss.writeObject(param);
                    socketChannel.write(buffer.wrap(byteArrayOutputStream.toByteArray()));
                    oss.flush();
                    this.setClient(socketChannel);
                    try {
                    	int index = socketChannel.read(buffer);
                    	/*BufferedReader reader = new BufferedReader(new InputStreamReader(socketChannel.socket().getInputStream()));
                    	System.out.println(reader.readLine());*/
                    	/* while(index <= 0) {
                    	logger.info("客户端接收信息："+dataArray.length);
                    	 index = socketChannel.read(buffer);
                    }*/
                    return (R) new String(buffer.array());
                    //ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer.array());
                    //ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
					//return (R) objectInputStream.readObject();
                    } catch (Exception e) {
                    	e.printStackTrace();
                    }
                    //socketChannel.register(selector, SelectionKey.OP_READ);
                    //ByteBuffer bytebuffer = ByteBuffer.allocate(1024);
/*                    int index = socketChannel.read(bytebuffer);
                    bytebuffer.flip();
                    byte[] by = new byte[index];
                    System.out.println(new String(by));*/
                    //bytebuffer.get(dataArray.length);
                   /* try {
						return receive();
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}*/
                }
            } catch (IOException e) {
                throw new ConnectionException("构建客户端失败",e);
            } 
        return null;
    }

    public R receive() throws ClassNotFoundException, IOException {
        ByteBuffer buffer = ByteBuffer.allocate(DefaultNetwork.DEFAULT_BUFFEER_SIZE);
        //buffer.flip();
        ByteArrayInputStream byteArrayInputStream = null;
        ObjectInputStream objectInputStream = null;
        try (
                SocketChannel socketChannel = SocketChannel.open();
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ){
             socketChannel.configureBlocking(block);
            //Selector selector = Selector.open();
            //getClient().register(selector, SelectionKey.OP_CONNECT);
            //logger.info("采用非阻塞模式：");
            //listener(selector);
                socketChannel.connect(new InetSocketAddress(getIp(),getPort()));
                System.out.println(getClient().isConnected());
                int index = this.getClient().read(buffer);
                
                InputStream is = socketChannel.socket().getInputStream();
                System.out.println(is.read());
                byte[] dataArray = new byte[index];
                System.out.println(dataArray.length);
                buffer.get(dataArray);
                logger.info("客户端接收信息："+dataArray.length);
                byteArrayInputStream = new ByteArrayInputStream(dataArray);
                objectInputStream = new ObjectInputStream(byteArrayInputStream);
                return (R) objectInputStream.readObject();
            //}
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            if(byteArrayInputStream != null){
                byteArrayInputStream.close();
            }
            if(objectInputStream != null){
                objectInputStream.close();
            }
        }
        return null;
    }

    /**
     * author:Alex/Libo
     * create-date:2018/11/23
     * create-time:20:55
     * method-name: handlerConnection
     * param: SelectionKey
     * return:
     * description: 处理连接操作
     */
    private void handlerConnection(SelectionKey selectionKey){
        if(selectionKey != null){
            try {
                ((ServerSocketChannel)selectionKey.channel()).accept().configureBlocking(false).
                        register(selectionKey.selector(),SelectionKey.OP_READ, ByteBuffer.allocate(DefaultNetwork.DEFAULT_BUFFEER_SIZE));
            } catch (IOException e) {
                throw new ConnectionException("建立请求应答连接失败！",e);
            }
        }
    }

    /**
     * author:Alex/Libo
     * create-date:2018/11/23
     * create-time:20:55
     * method-name: handlerConnection
     * param: SelectionKey
     * return:
     * description: 处理读请求操作
     */
    private byte[] handlerRead(SelectionKey selectionKey){
        SocketChannel channel = ((SocketChannel)selectionKey.channel());
        ByteBuffer bytebuffer = (ByteBuffer) selectionKey.attachment();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bytebuffer.clear();
        try {
            if(channel.read(bytebuffer) == -1){
                channel.close();
            }else{
                bytebuffer.flip();
                return bytebuffer.array();
            }
        } catch (IOException e) {
            throw new ConnectionException("读取连接数据信息失败！",e);
        }
        return new byte[0];
    }

    /**
     * author:Alex/Libo
     * create-date:2018/11/23
     * create-time:20:55
     * method-name: handlerConnection
     * param: SelectionKey
     * return:
     * description: 处理写请求操作
     */
    private void handlerWrite(SelectionKey selectionKey,Object obj){
        SocketChannel channel = (SocketChannel) selectionKey.channel();
        ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
        buffer.flip();
        try(ByteArrayOutputStream container = new ByteArrayOutputStream();
            ObjectOutputStream os = new ObjectOutputStream(container);
        ) {
            os.writeObject(obj);
            channel.write(ByteBuffer.wrap(container.toByteArray()));
            buffer.clear();
        } catch (IOException e) {
            throw new ConnectionException("客户端写入数据失败！",e);
        }
    }

    public void setParam(P param) {
        this.param = param;
    }
}
